<?php
/**
 * Copyright (c) 2006, PORTALIER Julien
 * 
 * Licensed under The LGPL License
 * Redistributions of files must retain the above copyright notice.
 * 
 * @package    FeatherPHP
 * @subpackage ACL
 * @copyright  Copyright (c) 2006, Julien PORTALIER
 * @link       http://portalier.com/feather
 * @license    http://www.opensource.org/licenses/lgpl-license.php The LGPL License
 */

$_acl = FEATHER.'controllers'.DS.'components'.DS.'acl'.DS;
require_once $_acl.'aclnode.php';
require_once $_acl.'aros_aco.php';
unset($_acl);

class AclComponent
{
	protected $controller;
	protected $session;
	
	// CRUD
	public    $Perms;
	protected $roles = array();
	
	// AUTH
	public $identified = false;
	public $id         = 'Anonymous';
	public $groups     = array('anonymous');
	
	function startup(Controller $controller=null)
	{
		$this->groupTable = array(
			'administrator' => _('Administrator'),
			'editor'        => _('Editor'),
			'author'        => _('Author'),
			'guest'         => _('Guest'),
			'subscriber'    => _('Subscriber'),
			'member'        => _('Member')
		);
		$this->controller = $controller;
		$this->session    = $controller->session;
		$this->Perms      = new ArosAco;
		
		// logs user in
		$this->login();
	}
	
	
	// user auth
	
	function login($user=null, $remember=false)
	{
		if (empty($user))
		{
			// has some login informations?
			if ($this->set())
				return;
			elseif (isset($this->params['form']['uid'], $this->params['form']['pwd']))
			{
				$uid = $this->params['form']['uid'];
				$pwd = $this->params['form']['pwd'];
			}
			elseif (Cookies::check(Config::auth_cookie)) {
				list($uid, $pwd) = Cookies::read(Config::auth_cookie);
			}
			
			// tries to login
			if (isset($uid, $pwd))
			{
				$Member = $this->controller->load->model('Member');
				$user   =& $Member->login($uid, $pwd);
				$user['pwd'] = $pwd;
			}
		}
		if (!empty($user))
		{
			$this->set(&$user);
			if ($remember)
				Cookies::write(Config::auth_cookie, array($this->id, $user['pwd']));
#			elseif (!isset($_COOKIE[Config::auth_cookie]))
#				Cookies::delete(Config::auth_cookie);
		}
	}
	
	function logout()
	{
		$this->identified = false;
		$this->session->delete('Member');
		Cookies::delete(Config::auth_cookie);
	}
	
	function set($user=null)
	{
		if (empty($user))
		{
			$user = $this->session->read('Member');
			if (empty($user)) return false;
		}
		else {
			$this->session->write('Member', &$user);
		}
		
		$this->identified = true;
		$this->id     = $user['id'];
		$this->groups = $user['groups'];
		
		return true;
	}
	
	
	// acls
	
	function inGroup()
	{
		$groups = func_get_args();
		if (func_num_args() == 1)
			$groups = explode(' ', $groups[0]);
		
		foreach($groups as $grp)
		{
			if (in_array($grp, $this->groups))
				return true;
		}
		return false;
	}
	
	/**
	 * @deprecated
	 */
	function isAdmin()
	{
		return $this->inGroup('administrator', 'editor', 'author', 'guest', 'moderator');
	}
	
	function checkAccess($aco, $action)
	{
		if (!$this->check($aco, null, $action)) {
			$this->permissionDenied();
		}
	}
	
	function check($aco=null, $aro=null, $action="read")
	{
		if (Config::power_privileges and $this->inGroup('administrator'))
			return true;
		
		$conditions = array('Aco.aco'=>$aco);
		if (!is_null($aro)) {
			$conditions['Aro.aro'] = $aro;		
		}
		$roles = $this->Perms->fetchRoles(&$conditions);
		
		// restricted access? checking rules
		if (!empty($roles))
		{
			// user specific rule?
			if (isset($roles["User.{$this->id}"]))
				return $roles["User.{$this->id}"][$action] ? true : false;
			
			// groups rules
			$right = null;
			foreach ($this->groups as $grp)
			{
				if (isset($roles["Group.$grp"]))
				{
					$right = $roles["Group.$grp"][$action];
					
					// access granted?
					if ($right) return true;
				}
			}
			if (!is_null($right))
				return $right ? true : false;
			
			// default rule?
			if (isset($roles['Group.all']))
				return $roles['Group.all'][$action] ? true : false;
			
			// access denied!
			return false;
		}
		
		// returns an undefined state
		return null;
	}
	
	function permissionDenied()
	{
		if ($this->identified)
		{
			$url = empty($this->controller->params['url']['referer']) ? '/' :
				$this->controller->params['url']['referer'];
			$this->controller->flash(_('Permission denied.'), $url, 403);
		}
		else {
			$this->controller->flash(_('Access is restricted, please sign in.'), '/members/login');
		}
	}
	
	
	// rights
	
	function allow($aro, $aco, $action='*', $value=true)
	{
		if ($action == '*')
		{
			$read   = $value;
			$write  = $value;
			$delete = $value;
		}
		else
		{
			foreach (explode(',', $action) as $action)
			{
				$action = trim($action);
				${$action} = $value;
			} 
		}
		return $this->Perms->create($aro, $aco,
			isset($read)   ? $read   : null,
			isset($write)  ? $write  : null,
			isset($delete) ? $delete : null
		);
	}
	
	function grant($aro, $aco, $action='*')
	{
		return $this->allow($aro, $aco, $action, true);
	}
	
	function deny($aro, $aco, $action='*')
	{
		return $this->allow($aro, $aco, $action, false);
	}
	
	function revoke($aro, $aco, $action='*')
	{
		return $this->allow($aro, $aco, $action, false);
	}
	
	function destroy($aco, $aro=null)
	{
		$this->Perms->destroy($aco, $aro);
	}
	
	function inherit($child, $parent)
	{
		// creating child ACO
		$aco_id = $this->Perms->Aco->create($child);
		
		// fetching roles for parent ACO
		$roles = $this->Perms->findAll(array('Aco.aco' => $parent));
		
		// copying parent's ACLs to child's
		foreach ($roles as $rule)
		{
			$rule['ArosAco']['aco_id'] = $aco_id;
			$rule['ArosAco']['id']     = null;
			$this->Perms->id = null;
			$this->Perms->__setData(&$rule);
			$this->Perms->save();
		}
	}
}
?>